package com.parking.mgr.useraccount.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.parking.mgr.common.cache.RedisCache;
import com.parking.mgr.common.constant.CommonConstant;
import com.parking.mgr.common.constant.TencentSmsConstant;
import com.parking.mgr.common.enums.CommonStatusEnum;
import com.parking.mgr.common.exception.LoginAuthException;
import com.parking.mgr.common.exception.ServiceException;
import com.parking.mgr.common.exception.enums.AuthExceptionEnum;
import com.parking.mgr.common.exception.enums.CommonExceptionEnum;
import com.parking.mgr.common.util.DesensitizationUtil;
import com.parking.mgr.common.util.IdUtils;
import com.parking.mgr.common.util.PwdCheckUtil;
import com.parking.mgr.common.util.TimestampUtil;
import com.parking.mgr.useraccount.facede.BasicRoleServiceFacede;
import com.parking.mgr.useraccount.facede.LoginServiceFacede;
import com.parking.mgr.useraccount.facede.SmsComponent;
import com.parking.mgr.useraccount.facede.UserServiceFacede;
import com.parking.mgr.useraccount.model.entity.*;
import com.parking.mgr.useraccount.model.mapper.UserInfoMapper;
import com.parking.mgr.useraccount.model.mapstruct.UpdateUserInfoMapStruct;
import com.parking.mgr.useraccount.model.mapstruct.UserInfoMapStruct;
import com.parking.mgr.useraccount.model.param.LoginTokenParam;
import com.parking.mgr.useraccount.model.param.UpdateUserInfoParam;
import com.parking.mgr.useraccount.model.param.UserInfoParam;
import com.parking.mgr.useraccount.model.param.UserRoleParam;
import com.parking.mgr.useraccount.model.vo.UserFullInfoVO;
import com.parking.mgr.useraccount.model.vo.UserHeadImgFileVO;
import com.parking.mgr.useraccount.model.vo.UserInfoVO;
import com.parking.mgr.useraccount.service.*;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName BasicUserInfoServiceImpl
 * @Description TODO
 * @Auther: zhijianpu
 * @Date: 2023/5/25
 */
@Log4j2
@Service
public class UserInfoServiceImpl  extends ServiceImpl<UserInfoMapper, UserInfoEntity> implements UserInfoService {

    @Resource
    private UserServiceFacede userServiceFacede;

    @Resource
    private LoginServiceFacede loginServiceFacede;

    @Resource
    private BasicRoleServiceFacede basicRoleServiceFacede;

    @Resource
    private UserInfoMapper userInfoMapper;

    @Resource
    private UserInfoMapStruct userInfoMapStruct;

    @Resource
    private UpdateUserInfoMapStruct updateUserInfoMapStruct;

    @Resource
    private SmsComponent smsComponent;

    @Resource
    private RedisCache redisCache;

    @Resource
    private UserPhoneService userPhoneService;

    @Resource
    private LoginPasswordService loginPasswordService;

    @Resource
    private UserAccountService userAccountService;

    @Resource
    private BasicRoleService basicRoleService;

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd/");


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addUserInfo(UserInfoParam userInfoParam) {

        // 1. 判断验证码是否正确
        String mobilePhoneNumber = userInfoParam.getFMobilePhoneNumber();
        // 查询用户是否存在
        UserPhoneEntity userPhoneEntity = userPhoneService.getUserInfoByPhone(mobilePhoneNumber);
        if (ObjectUtil.isNotNull(userPhoneEntity)) {
            throw new ServiceException("手机号已经注册，请勿重复注册");
        }

        String code = userInfoParam.getCode();
        String redisCode = redisCache.getCacheObject(CommonConstant.SMS_HEAD + userInfoParam.getFMobilePhoneNumber());
        if (StringUtils.isEmpty(redisCode)) {
            throw new ServiceException("验证码失效");
        }
        if (!StringUtils.equals(redisCode.split("_")[0], code)) {
            throw new ServiceException("验证码错误");
        }


        // 2. 判断两次密码是否一致
        if (!StringUtils.equals(userInfoParam.getFPassword(), userInfoParam.getConfirmPassword())) {
            throw new ServiceException("两次密码不一致");
        }

        redisCache.deleteObject(CommonConstant.SMS_HEAD + mobilePhoneNumber);

        // 保存用户账号
        String passportId = userServiceFacede.add();

        // 保存用户信息
        UserInfoEntity userInfoEntity = new UserInfoEntity();
        userInfoEntity.setFkUserPassportId(passportId);
        this.save(userInfoEntity);

        // 保存用户账户
        UserAccountEntity userAccountEntity = new UserAccountEntity();
        userAccountEntity.setFkUserPassportId(passportId);
        userAccountEntity.setFAccount(mobilePhoneNumber);
        userAccountService.save(userAccountEntity);

        // 保存用户手机
        userPhoneService.saveUserPhone(passportId, "+86", mobilePhoneNumber);

        // 保存用户密码
        loginPasswordService.savePwd(userInfoParam.getFPassword(), passportId);

        // 保存用户角色
        String roleId = basicRoleServiceFacede.getRoleIdByName("备案人员");
        BasicRoleUserEntity basicRoleUserEntity = new BasicRoleUserEntity();
        basicRoleUserEntity.setFkUserPassportId(passportId);
        basicRoleUserEntity.setFkBasicRoleId(roleId);
        basicRoleServiceFacede.save(basicRoleUserEntity);
    }

    @Override
    public void updateUserPhone(UserPhoneEntity userPhoneEntity) {
        // 对敏感字段（手机号进行脱敏）并更新
        if (ObjectUtil.isNotEmpty(userPhoneEntity.getFMobilePhoneNumber())) {
            String mobilePhoneMask = DesensitizationUtil.commonDisplay(userPhoneEntity.getFMobilePhoneNumber());
            LambdaUpdateWrapper<UserInfoEntity> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(UserInfoEntity::getFkUserPassportId, userPhoneEntity.getFkUserPassportId())
                    .set(UserInfoEntity::getFMobilePhoneMask, mobilePhoneMask)
                    .set(UserInfoEntity::getFUpdateTimeMillis, TimestampUtil.createTimestamp());
            boolean updateUserPhone = this.update(updateWrapper);

            if (!updateUserPhone) {
                throw new ServiceException(CommonExceptionEnum.DATA_NOT_EXIST.getMessage());
            }
        }

    }

    @Override
    public UserFullInfoVO detail(HttpServletRequest request) {
        LambdaQueryWrapper<UserInfoEntity> queryWrapper = new LambdaQueryWrapper<>();
        String passportId = loginServiceFacede.getPassportIdByToken(request);

        queryWrapper.eq(UserInfoEntity::getFkUserPassportId, passportId);
        UserFullInfoVO userFullInfoVO = userInfoMapper.selectUserInfoDetail(queryWrapper);

        if (ObjectUtil.isNull(userFullInfoVO)) {
            throw new LoginAuthException(AuthExceptionEnum.NO_LOGIN_USER.getMessage());
        }

        return userFullInfoVO;
    }

    @Override
    public UserFullInfoVO getUserByUserInfoId(String id) {
        LambdaQueryWrapper<UserInfoEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserInfoEntity::getId, id);

        UserFullInfoVO userFullInfoVO = userInfoMapper.selectUserInfoDetail(queryWrapper);

        if (ObjectUtil.isNotNull(userFullInfoVO)) {
            return userFullInfoVO;
        } else {
            return null;
        }
    }

    @Override
    public void editUserInfo(UpdateUserInfoParam updateUserInfoParam) {
        UserInfoEntity userInfoEntity = updateUserInfoMapStruct.param2Entity(updateUserInfoParam);
        userInfoMapper.updateById(userInfoEntity);
    }

    @Override
    public void editUserSelfInfo(UpdateUserInfoParam updateUserInfoParam, HttpServletRequest request) {
        log.info("编辑登录用户信息");
        String passportId = loginServiceFacede.getPassportIdByToken(request);
        log.info("用户所属账号: " + passportId);
        log.info("获取登录用户信息");
        UserInfoEntity userInfoEntity = this.getUserInfoByPassportId(passportId);

        if (ObjectUtil.isNull(userInfoEntity)) {
            throw new ServiceException(AuthExceptionEnum.NOT_USER_INFO.getMessage());
        }

        userInfoEntity.setFBirthdayTimeMillis(updateUserInfoParam.getFBirthdayTimeMillis());
        userInfoEntity.setFTrueName(updateUserInfoParam.getFTrueName());
        userInfoEntity.setFGender(updateUserInfoParam.getFGender());

        userInfoMapper.updateById(userInfoEntity);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteUserInfo(Collection<String> ids) {
        if (CollectionUtil.isNotEmpty(ids)) {
            for (String id : ids) {
                // 获取用户passportId
                UserInfoEntity userInfoEntity = userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfoEntity>()
                        .eq(UserInfoEntity::getId, id));
                String passportId = userInfoEntity.getFkUserPassportId();
                // 删除用户账户
                userServiceFacede.deleteAccountByPassportId(passportId);
                // 删除用户信息
                userInfoMapper.deleteById(id);
                // 删除用户手机号
                userServiceFacede.deleteUserPhoneByPassportId(passportId);
                // 删除用户密码
                loginServiceFacede.deletePasswordByPassportId(passportId);
            }
        }
    }

    @Override
    public IPage<UserInfoVO> listUserInfo(UserInfoParam userInfoParam) {
        LambdaQueryWrapper<UserInfoEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserInfoEntity::getFEnableState, CommonStatusEnum.ENABLE.getCode())
                        .orderByAsc(UserInfoEntity::getFInsertTimeMillis);
        IPage<UserInfoVO> userInfoList = userInfoMapStruct.entity2VO(this.page(new Page<>(userInfoParam.getCurrPage(), userInfoParam.getPageSize()), queryWrapper));

        return userInfoList;
    }

    @Override
    public IPage<UserFullInfoVO> listUserInfoFull(UserInfoParam userInfoParam) {
        LambdaQueryWrapper<UserInfoEntity> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.eq(UserInfoEntity::getFEnableState, CommonStatusEnum.ENABLE.getCode())
                .orderByAsc(UserInfoEntity::getFInsertTimeMillis);
        IPage<UserFullInfoVO> userFullInfoVOIPage = userInfoMapper.selectFullUserInfoPage(new Page<>(userInfoParam.getCurrPage(), userInfoParam.getPageSize()), queryWrapper);
        return userFullInfoVOIPage;
    }

    @Override
    public UserInfoEntity getUserInfoById(String id) {

        UserInfoEntity userInfoEntity = this.getById(id);

        if (ObjectUtil.isNull(userInfoEntity)) {
            throw new ServiceException(AuthExceptionEnum.NOT_USER_INFO.getMessage());
        }


        return userInfoEntity;
    }

    @Override
    public UserInfoEntity getUserInfoByPassportId(String passportId) {
        UserInfoEntity userInfoEntity = userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfoEntity>()
                .eq(UserInfoEntity::getFkUserPassportId, passportId)
                .eq(UserInfoEntity::getFEnableState, CommonStatusEnum.ENABLE.getCode()));

        return userInfoEntity;
    }

    @Override
    public void setRoleUser(UserRoleParam userRoleParam) {
        log.info("为用户分配角色");
        String userInfoId = userRoleParam.getId();
        UserInfoEntity userInfoEntity = this.getUserInfoById(userInfoId);
        String passportId = userInfoEntity.getFkUserPassportId();
        log.info("获取用户现有角色");
        List<BasicRoleUserEntity> userRoleEntities = basicRoleServiceFacede.getRoleUserEntityList(passportId);
        log.info("删除用户现有角色");
        if (CollectionUtil.isNotEmpty(userRoleEntities)) {
            List<String> userRoleIds = new ArrayList<>(userRoleEntities.size());
            for (BasicRoleUserEntity roleUserEntity : userRoleEntities) {
                userRoleIds.add(roleUserEntity.getId());
            }

            basicRoleServiceFacede.deleteBatchUserRolesById(userRoleIds);
            log.info("删除用户现有角色=============完成");
        }

        List<String> roleIds = userRoleParam.getFkBasicRoleIds();

        if (CollectionUtil.isNotEmpty(roleIds)) {
            for (String id : roleIds) {
                log.info("为用户分配角色");
                BasicRoleUserEntity basicRoleUserEntity = new BasicRoleUserEntity();
                basicRoleUserEntity.setFkBasicRoleId(id);
                basicRoleUserEntity.setFkUserPassportId(passportId);
                basicRoleServiceFacede.save(basicRoleUserEntity);
            }
        } else {
            log.info("传入的角色列表ID为空，不进行保存操作");
        }

    }

    @Override
    public List<String> roleUserList(String userInfoId) {
        UserInfoEntity userInfoEntity = this.getUserInfoById(userInfoId);
        String passportId = userInfoEntity.getFkUserPassportId();

        return basicRoleServiceFacede.getRoleUserList(passportId);
    }

    @Override
    public UserHeadImgFileVO uploadHeadImg(MultipartFile uploadFile, HttpServletRequest request, String uploadPath) {
        String passportId = loginServiceFacede.getPassportIdByToken(request);
        log.info("上传用户头像，用户ID为： " + passportId);
        UserInfoEntity userInfoEntity = this.getUserInfoByPassportId(passportId);
        // 在 uploadPath 文件夹中通过日期对上传的文件归类保存
        // 比如：/2019/06/06/cf13891e-4b95-4000-81eb-b6d70ae44930.png

        String format = sdf.format(new Date());
        File folder = new File(uploadPath + format + "/" + CommonConstant.HEAD_IMG_FILE_PATH +"/");
        if (!folder.isDirectory()) {
            folder.mkdirs();
        }

        // 对上传的文件进行重命名，避免文件重名
        String oldName = uploadFile.getOriginalFilename();
        String fileId = IdUtils.getId();
        // 上传文件可能没有后缀名
        int i = oldName.lastIndexOf(".");
        String fileSuffix = "";
        if (i > 0) {
            fileSuffix = oldName.substring(oldName.lastIndexOf("."));
        }

        String newName = fileId + fileSuffix;

        try {

            // 文件保存
            uploadFile.transferTo(new File(folder, newName));

            // 返回上传文件的访问路径
            String filePath = request.getScheme() + "://" + request.getServerName()
                    + ":" + request.getServerPort()  + "/" + CommonConstant.HEAD_IMG_FILE_PATH +  "/" + newName;


            userInfoEntity.setFHeadImgFileId(fileId);
            userInfoEntity.setFHeadImgFileSuffix(fileSuffix);
            userInfoEntity.setFHeadImgUrl(folder.getPath() + "/");
            this.updateById(userInfoEntity);

            UserHeadImgFileVO userHeadImgFileVO = new UserHeadImgFileVO();
            userHeadImgFileVO.setId(fileId);
            userHeadImgFileVO.setFHeadImgFileSuffix(fileSuffix);
            userHeadImgFileVO.setFHeadImgUrl(folder.getPath() + "/");


            return userHeadImgFileVO;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    public List<String> getRoleUserMenuList(HttpServletRequest request) {
        log.info("获取登录用户所属角色的功能菜单列表");
        String passportId = loginServiceFacede.getPassportIdByToken(request);
        log.info("用户账号为: " + passportId);
        // 获取用户角色ID
        List<String> userRoleIds = basicRoleServiceFacede.getRoleUserList(passportId);

        // 获取用户所属角色菜单ID
        return basicRoleServiceFacede.getRoleUserMenuList(userRoleIds);
    }

    @Override
    public boolean isRoleUserMenu(HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        List<String> urlList = this.getRoleUserMenuList(request);

        return CollectionUtil.contains(urlList, requestURI);
    }

    @Override
    public byte[] getUserHeadImageFile(String id, HttpServletResponse response) throws IOException {
        // 通过文件ID获取用户信息实体
        UserInfoEntity userInfoEntity = userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfoEntity>()
                .eq(UserInfoEntity::getFHeadImgFileId, id));

        if (ObjectUtil.isNotNull(userInfoEntity)) {
            String headImageUrl = userInfoEntity.getFHeadImgUrl();
            String headImgFileSuffix = userInfoEntity.getFHeadImgFileSuffix();
            String filePath = headImageUrl + id + headImgFileSuffix;


            File headImgFile = new File(headImageUrl + id + headImgFileSuffix);
            // 这是返回文件流方法
//            if (headImgFile.exists()) {
//                return ResponseEntity.ok()
//                        .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition)
//                        .contentType(MediaType.APPLICATION_OCTET_STREAM)
//                        .body(new FileSystemResource(filePath));
//            } else {
//                return ResponseEntity.badRequest()
//                        .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition + " Not Found")
//                        .contentType(MediaType.APPLICATION_OCTET_STREAM)
//                        .body(null);
//            }
            // 将图像返回，禁止验证码图片缓存
            FileInputStream inputStream = new FileInputStream(headImgFile);
            byte[] bytes = new byte[inputStream.available()];
            inputStream.read(bytes, 0, inputStream.available());
            return bytes;
        }
        return null;
    }

    @Override
    public UserHeadImgFileVO uploadHeadImgFileSys(MultipartFile uploadFile, HttpServletRequest request) {
        String passportId = loginServiceFacede.getPassportIdByToken(request);
        log.info("上传用户头像，用户ID为： " + passportId);
        UserInfoEntity userInfoEntity = this.getUserInfoByPassportId(passportId);

        UserHeadImgFileVO userHeadImgFileVO = new UserHeadImgFileVO();


        return userHeadImgFileVO;
    }

    @Override
    public byte[] getHeadImageFileByFileSys(String fHeadImgId) throws IOException {

        Map<String, Object> signatureMap = MapUtil.newHashMap();


        byte[] image = null;

        return image;
    }

    @Override
    public String getSmsCode(String phone) {
        if (phone == null || phone.length() == 0) {
            throw new ServiceException("手机号为空");
        }

        int leastTime = 5 * 60 * 1000;

        // 判断是否已经发送过
        String redisCode = redisCache.getCacheObject(CommonConstant.SMS_HEAD + phone);
        if (!StringUtils.isEmpty(redisCode)) {
            long time = Long.parseLong(redisCode.split("_")[1]);
            if (System.currentTimeMillis() - time < leastTime) {
                throw new ServiceException("发送频率过高");
            }
        }
        String code = PwdCheckUtil.getSixBitCode();  // 生成新的验证码
        //存储 phone->code
        redisCache.setCacheObject(CommonConstant.SMS_HEAD + phone, code + "_" + System.currentTimeMillis(), TencentSmsConstant.TIMEOUT, TimeUnit.MINUTES);
        Integer minute = leastTime / 60 / 1000; //分钟
        smsComponent.sendCode(phone, code, minute.toString());
        return "已发送验证码 " + phone;
    }

    @Override
    public String logout(HttpServletRequest request) {

        // 获取token
        String token = request.getHeader(CommonConstant.LOGIN_TOKEN);
        String clientId = request.getHeader(CommonConstant.CLIENT_ID);

        LoginTokenEntity loginTokenEntity = loginServiceFacede.getLoginTokenEntity(token, clientId);

        // 删除token信息
        loginServiceFacede.delete(token, clientId);
        // 生成新的clientId
        clientId = IdUtils.getId();
        LoginTokenParam loginTokenParam = new LoginTokenParam();
        loginTokenParam.setFClientId(clientId);
        loginServiceFacede.add(loginTokenParam, request);

        return clientId;
    }


}
